/*
 * CFileUploadQueue.cpp
 *
 *  Created on: 2017年11月8日
 *      Author: zhengboyuan
 */

#include "CFileUploadQueue.h"
#include "CFileUploadClient.h"

CFileUploadQueue::CFileUploadQueue():
	m_listener(),
	m_nextTaskId(1)
{
}

CFileUploadQueue::~CFileUploadQueue()
{
	stop();
}

bool CFileUploadQueue::start()
{
	if (isRunning())
	{
		return true;
	}

	return comn::Thread::start();
}

void CFileUploadQueue::stop()
{
	if (isRunning())
	{
		comn::Thread::stop();
	}

	{
		comn::AutoCritSec lock(m_cs);
		m_tasks.clear();
	}
}

int CFileUploadQueue::add(int tid, const std::string& url, const StringMap& header, const StringMap& formData, const FileUpload& file)
{
	if (tid > 0)
	{
		if (existTaskId(tid))
		{
			return -1;
		}
	}
	else
	{
		tid = makeTaskId();
	}

	if (url.empty())
	{
		return -1;
	}

	if (!file.check())
	{
		return -1;
	}

	FileUploadTask task;
	task.id = tid;
	task.url = url;
	task.headers = header;
	task.formData = formData;
	task.files.push_back(file);

	return addTask(task);
}

int CFileUploadQueue::addMulti(int tid, const std::string& url, const StringMap& header, const StringMap& formData, const FileUploadArray& files)
{
	if (tid > 0)
	{
		if (existTaskId(tid))
		{
			return -1;
		}
	}
	else
	{
		tid = makeTaskId();
	}

	if (url.empty())
	{
		return -1;
	}

	if (files.empty())
	{
		return -1;
	}

	FileUploadTask task;
	task.id = tid;
	task.url = url;
	task.headers = header;
	task.formData = formData;
	task.files = files;

	return addTask(task);
}

void CFileUploadQueue::cancel(int tid)
{
	FileUploadTask task;
	removeTask(tid, task);

	postCommand(kCmdRemoveTask, tid);

}

void CFileUploadQueue::setListener(Listener* listener)
{
	comn::AutoCritSec lock(m_cs);
	m_listener = listener;
}

void CFileUploadQueue::onCommand(util::Command& cmd)
{
	if (cmd.type == kCmdAddTask)
	{
		handleAddTask();
	}
	else if (cmd.type == kCmdRemoveTask)
	{
		int tid = cmd[0].toInt();
		handleCancelUpload(tid);
	}
	else if (cmd.type == kCmdUploadEnd)
	{
		int tid = cmd[0].toInt();
		handleUploadEnd(tid);
	}
	else if (cmd.type == kCmdUploadError)
	{
		int tid = cmd[0].toInt();
		handleUploadError(tid);
	}
	else
	{
		/// unknown command
	}
}

int CFileUploadQueue::makeTaskId()
{
	int tid = m_nextTaskId ++;
	while (existTaskId(tid))
	{
		tid = m_nextTaskId ++;
	}
	return tid;
}

bool CFileUploadQueue::existTaskId(int tid)
{
	comn::AutoCritSec lock(m_cs);
	for (size_t i = 0; i < m_tasks.size(); ++ i)
	{
		if (m_tasks[i].id == tid)
		{
			return true;
		}
	}
	return false;
}

bool CFileUploadQueue::removeTask(int tid, FileUploadTask& task)
{
	bool found = false;
	comn::AutoCritSec lock(m_cs);
	FileUploadTaskDeque::iterator it = m_tasks.begin();
	for (; it != m_tasks.end(); ++ it)
	{
		if (it->id == tid)
		{
			task = *it;
			m_tasks.erase(it);
			found = true;
			break;
		}
	}
	return found;
}

void CFileUploadQueue::handleCancelUpload(int tid)
{
	if (m_client && m_client->getId() == tid)
	{
		m_client->cancel();
		m_client.reset();
	}
}

void CFileUploadQueue::handleAddTask()
{
	if (!m_client)
	{
		tryStartTask();
	}
}

void CFileUploadQueue::handleUploadEnd(int tid)
{
	if (tid == m_curTask.id)
	{
		m_client.reset();
		tryStartTask();
	}
}

void CFileUploadQueue::handleUploadError(int tid)
{
	if (tid == m_curTask.id)
	{
		m_client.reset();
		tryStartTask();
	}
}

bool CFileUploadQueue::waitForTask(FileUploadTask& task, int ms)
{
	bool got = fetchTask(task);
	if (!got)
	{
		timedwait(ms);
		got = fetchTask(task);
	}
	return got;
}

bool CFileUploadQueue::fetchTask(FileUploadTask& task)
{
	bool found = false;
	comn::AutoCritSec lock(m_cs);
	if (m_tasks.size() > 0)
	{
		task = m_tasks.front();
		m_tasks.pop_front();
		found = true;
	}
	return found;
}

bool CFileUploadQueue::tryStartTask()
{
	bool got = fetchTask(m_curTask);
	if (got)
	{
		FileUploadTask& task = m_curTask;

		m_client.reset(new CFileUploadClient());
		m_client->setSink(this);
		m_client->setId(task.id);
		m_client->upload(task.url, task.headers, task.formData, task.files);
	}
	return got;
}

int CFileUploadQueue::addTask(FileUploadTask& task)
{
	m_tasks.push_back(task);

	postCommand(kCmdAddTask);

	return task.id;
}

void CFileUploadQueue::onFileUploadStatus(FileUploadClient* client, int status, const std::string& result)
{
	int tid = client->getId();

	if (m_listener)
	{
		if (m_curTask.id == tid)
		{
			m_listener->onFileUploadStatus(tid, m_curTask, status, result);
		}
		else
		{
			FileUploadTask task;
			task.id = tid;
			task.url = client->getUrl();

			m_listener->onFileUploadStatus(tid, task, status, result);
		}
	}

	if (status == kUploadEnd)
	{
		postCommand(kCmdUploadEnd, tid);
	}
	else if (status == kUploadError)
	{
		postCommand(kCmdUploadError, tid);
	}
}

void CFileUploadQueue::onFileUploadProgress(FileUploadClient* client, int total, int sent)
{
	int tid = client->getId();

	if (m_listener)
	{
		m_listener->onFileUploadProgress(tid, m_curTask, total, sent);
	}
}
